package com.mayjean.redis.limit;

import cn.hutool.core.util.StrUtil;
import com.mayjean.common.NezhaConstant;
import com.mayjean.common.exception.I18nException;
import com.mayjean.common.i18n.Status;
import com.mayjean.common.spel.AbstractExpressionEvaluator;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * Rate limit aspect
 *
 * @author
 */
@Slf4j
@Aspect
@RequiredArgsConstructor
@Component
public class RateLimitAspect extends AbstractExpressionEvaluator {

	private final IRedisLimit redisLimitService;

	@Around("@annotation(com.mayjean.redis.limit.RateLimit))")
	public Object around(ProceedingJoinPoint pjp) throws Throwable {
		Method method = ((MethodSignature) pjp.getSignature()).getMethod();
		RateLimit rateLimit = method.getAnnotation(RateLimit.class);
		String key = rateLimit.key();
		if (StrUtil.isBlank(key)) {
			throw new I18nException(Status.Redis_Limit_Key_Is_Blank);
		}

		key = NezhaConstant.nezhaPreFix(key);

		if (StrUtil.isNotBlank(rateLimit.params())) {
			String evalParam = evalSePlParam(pjp, rateLimit.params());
			if (StrUtil.isNotBlank(evalParam)) {
				key = key + StrUtil.C_UNDERLINE + evalParam;
			}
		}

		Long limit = redisLimitService.limit(key, rateLimit.max(), rateLimit.ttl(), rateLimit.timeUnit());
		if (limit == 0) {
			throw new I18nException(Status.Redis_Limit_Is_Limit);
		}

		try {
			Object rawResult = pjp.proceed();
			return rawResult;
		} catch (Exception ex) {
			throw ex;
		}
	}
}
